/*
Copyright 2010  Murali Devi (profdevi@gmail.com)

This file is part of ComineGL Project.

ComineGL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

ComineGL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with ComineGL.  If not, see <http://www.gnu.org/licenses/>.

This code uses the irrlicht and irrklang libraries.  Please read
the libraries copyrights at 

	irrlicht library:  http://irrlicht.sourceforge.net
	irrklang library:  http://www.ambiera.com/irrklang

*/



//v1.5 copyright Comine.com 20101211S22
#include "MStdLib.h"
#include "MIrrlicht.h"
#include "MCGLNodeInfo.h"


//******************************************************
//**  ModuleElements
//******************************************************
struct GCGLNodeTypeNames
	{
	MCGLNodeInfoType Type;
	const char *Description;
	};

////////////////////////////////////////////////
static GCGLNodeTypeNames GTypeNames[]=
		{
		CGLNIT_CUBE,"Cube",
		CGLNIT_SPHERE,"Sphere",
		CGLNIT_TEXT,"Text",
		CGLNIT_WATER_SURFACE,"Water Surface",
		CGLNIT_TERRAIN,"Terrain",
		CGLNIT_SKY_BOX,"Box",
		CGLNIT_SKY_DOME,"Dome",
		CGLNIT_SHADOW_VOLUME,"Shadow Volume",
		CGLNIT_OCT_TREE,"Tree",
		CGLNIT_MESH,"Mesh",
		CGLNIT_LIGHT,"Light",
		CGLNIT_EMPTY,"Empty",
		CGLNIT_DUMMY_TRANSFORMATION,"Transformation",
		CGLNIT_CAMERA,"Camera",
		CGLNIT_CAMERA_MAYA,"Camera(Maya)",
		CGLNIT_CAMERA_FPS,"Camers(FPS)",
		CGLNIT_BILLBOARD,"Bill Board",
		CGLNIT_ANIMATED_MESH,"Animated Mesh",
		CGLNIT_PARTICLE_SYSTEM,"Particle System",
		CGLNIT_FONT_TEXT,"Font Text",
		CGLNIT_LOD,"Level of Detail"
		};


////////////////////////////////////////////////
static bool GRemoveAnimator(ISceneNode *node,ESCENE_NODE_ANIMATOR_TYPE animtype)
	{
	MStdAssert(node!=NULL);

	const ISceneNodeAnimatorList &itlist=node->getAnimators();
	ISceneNodeAnimatorList::ConstIterator i;

	ISceneNodeAnimatorList removelist;
	
	// Find All Animators that match animtype
	for(i=itlist.begin(); i!=itlist.end() ; ++i)
		{
		if( (*i)->getType()==animtype)
			{
			removelist.push_back(*i);
			}
		}

	// Now remove them from animator list
	for(i=removelist.begin();i!=removelist.end();++i)
		{
		node->removeAnimator(*i);
		}

	return true;
	}


//******************************************************
//**  MCGLNodeInfo class
//******************************************************
void MCGLNodeInfo::ClearObject(void)
	{
	mSceneMgr=NULL;
	mNode=NULL;
	mRemoveOnDelete=false;
	mNodeType=(MCGLNodeInfoType)0;
	mLODCallBack=NULL;
	mLODCallBackExtra=NULL;
	mVelocityX=0.0f;
	mVelocityY=0.0f;
	mVelocityZ=0.0f;
	mRotationalX=0.0f;
	mRotationalY=0.0f;
	mRotationalZ=0.0f;
	}


////////////////////////////////////////////////
MCGLNodeInfo::MCGLNodeInfo(void)
	{  ClearObject();  }


////////////////////////////////////////////////
MCGLNodeInfo::MCGLNodeInfo(ISceneManager *scnmgr,ISceneNode *node,MCGLNodeInfoType type)
	{
	ClearObject();
	if(Create(scnmgr,node,type)==false)
		{
		return;
		}
	}

////////////////////////////////////////////////
MCGLNodeInfo::~MCGLNodeInfo(void)
	{  Destroy();  }


////////////////////////////////////////////////
bool MCGLNodeInfo::Create(ISceneManager *scnmgr,ISceneNode *node,MCGLNodeInfoType type)
	{
	Destroy();

	if(node==NULL || scnmgr==NULL)
		{
		Destroy();
		return false;
		}

	mSceneMgr=scnmgr;
	mSceneMgr->grab();

	mNode=node;
	mNode->grab();

	mRemoveOnDelete=true;
	mNodeType=type;

	return true;
	}


////////////////////////////////////////////////
bool MCGLNodeInfo::Destroy(void)
	{
	if(mNode!=NULL)
		{
		if(mRemoveOnDelete==true)
			{
			mSceneMgr->addToDeletionQueue(mNode);
			}

		mNode->drop();
		mNode=NULL;
		}

	if(mSceneMgr!=NULL)
		{
		mSceneMgr->drop();
		}

	ClearObject();
	return true;
	}


////////////////////////////////////////////////
ISceneNode *MCGLNodeInfo::GetNode(void)
	{
	return mNode;
	}


///////////////////////////////////////////////
bool MCGLNodeInfo::SetRemoveOnDelete(bool value)
	{
	mRemoveOnDelete=value;
	return true;
	}


////////////////////////////////////////////////
MCGLNodeInfoType MCGLNodeInfo::GetNodeType(void)
	{
	return mNodeType;
	}


/////////////////////////////////////////////////
bool MCGLNodeInfo::SetLODCallBack(bool lodcallback(class MSGAEngine3D &engine
		,float cameradistance2,int nodeid,void *extra),void *extra)
	{
	mLODCallBack=lodcallback;
	mLODCallBackExtra=extra;
	return true;
	}


/////////////////////////////////////////////////
bool MCGLNodeInfo::GetLODCallBack(bool (*&lodcallback)(class MSGAEngine3D &engine
		,float cameradistance2,int nodeid,void *extra),void *&extra)
	{
	lodcallback=mLODCallBack;
	extra=mLODCallBackExtra;
	return true;
	}


/////////////////////////////////////////////////
const char *MCGLNodeInfo::GetTypeName(void)
	{
	MStdAssert(mNodeType>=0 && mNodeType<=CGLNIT_LOD);
	MStdAssert(GTypeNames[mNodeType].Type==mNodeType);
	return GTypeNames[mNodeType].Description;
	}


/////////////////////////////////////////////////
MCGLNodeInfoType MCGLNodeInfo::GetType(void)
	{
	return mNodeType;
	}


/////////////////////////////////////////////////
bool MCGLNodeInfo::ClearRotation(void)
	{
	GRemoveAnimator(mNode,ESNAT_ROTATION);
	mRotationalX=0.0f;
	mRotationalY=0.0f;
	mRotationalZ=0.0f;	
	return true;
	}


/////////////////////////////////////////////////
bool MCGLNodeInfo::ClearVelocity(void)
	{
	GRemoveAnimator(mNode,ESNAT_FLY_STRAIGHT);
	mVelocityX=0.0f;
	mVelocityY=0.0f;
	mVelocityZ=0.0f;		
	return true;
	}


/////////////////////////////////////////////////
bool MCGLNodeInfo::SetRotation(const float &rx,const float &ry,const float &rz)
	{
	// Remove Existing Rotation Animator
	GRemoveAnimator(mNode,ESNAT_ROTATION);

	ISceneNodeAnimator *animator=
			mSceneMgr->createRotationAnimator(vector3df(rx,ry,rz));
	if(animator==NULL)
		{
		mRotationalX=0.0f;
		mRotationalY=0.0f;
		mRotationalZ=0.0f;
		return false;
		}

	mNode->addAnimator(animator);
	animator->drop();

	// Update Rotation
	mRotationalX=rx;
	mRotationalY=ry;
	mRotationalZ=rz;

	return true;
	}


/////////////////////////////////////////////////
bool MCGLNodeInfo::SetVelocity(const float &vx,const float &vy,const float &vz)
	{
	// Remove Existing Rotation Animator
	GRemoveAnimator(mNode,ESNAT_FLY_STRAIGHT);

	// Get Node Position
	vector3df position=mNode->getPosition();

	// Calculate target position
	const u32 time=1000000000;
	vector3df targetpos=position+vector3df(vx,vy,vz)*time;
	
	ISceneNodeAnimator *animator=
			mSceneMgr->createFlyStraightAnimator(position,targetpos,time);
	if(animator==NULL)
		{
		mVelocityX=0.0f;
		mVelocityY=0.0f;
		mVelocityZ=0.0f;
		return false;
		}

	mNode->addAnimator(animator);
	animator->drop();

	mVelocityX=vx;
	mVelocityY=vy;
	mVelocityZ=vz;

	return true;
	}


/////////////////////////////////////////////////////////
bool MCGLNodeInfo::ClearAnimators(void)
	{
	mNode->removeAnimators();

	mVelocityX=0;
	mVelocityY=0;
	mVelocityZ=0;

	mRotationalX=0;
	mRotationalY=0;
	mRotationalZ=0;

	return true;
	}
